/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  FormatTable.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2016/08/03
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#include "FormatTable.h"

#include <stdlib.h>

#include <map>
#include <vector>

#include "../system/system_util.h"
#include "string_util.h"

namespace qb {
namespace base {
typedef std::map<int, BaseString> Record;
typedef std::map<BaseString, int> IndexMap;
namespace {
bool IsRefColumn(const BaseString& name) {
  return name.size() > 0 && (name.at(0) == '@' || name.at(0) == '#');
}
double ToDouble(Record* precord, int col) {
  if (precord) {
    Record::iterator it = precord->find(col);
    if (it != precord->end()) return atof(it->second.c_str());
  }
  return 0.0;
}
void UpdateBaseTimes(int i, int col, std::vector<Record*>& records) {
  int nrow = records.size();
  if (nrow < 0) return;
  double base = ToDouble(records[0], col);
  for (int k = 0; k < nrow; k++) {
    Record* record = records.at(k);
    double d = ToDouble(record, col);
    (*record)[i] = os::fmtstr("%4.2f", base == 0 ? 0 : (d / base));
  }
}
void UpdatePercent(int i, int col, std::vector<Record*>& records) {
  double sum = 0;
  int nrow = records.size();
  for (int k = 0; k < nrow; k++) {
    Record* record = records.at(k);
    sum += ToDouble(record, col);
  }
  for (int k = 0; k < nrow; k++) {
    Record* record = records.at(k);
    double d = ToDouble(record, col);
    (*record)[i] = os::fmtstr("%4.2f%%", sum == 0 ? 0 : (d * 100.0 / sum));
  }
}
}  // namespace

#if defined(CCORE_MAC)
struct sort_r_data {
  void* arg;
  int (*compar)(const void* a1, const void* a2, void* aarg);
};
int sort_r_arg_swap(void* s, const void* aa, const void* bb) {
  struct sort_r_data* ss = (struct sort_r_data*)s;
  return (ss->compar)(aa, bb, ss->arg);
}
#endif

class TableColumn {
 public:
  BaseString name;  // 该列的名称
  int maxlen;       // 该列的字符串的最大宽度
  int count;  // 该列被设置了就递增,每当设置了超过1此就换行
};
class TableGroup {
  BaseString m_name;                   // 分组名称
  std::vector<TableColumn> m_columns;  // 列信息
  std::vector<Record*> m_table;        // 记录行
  IndexMap m_indexmap;                 // 列索引
  BaseString m_sort_col;               //
  bool m_sort_ascend;                  // 要排序的列是倒序还是反序
  int m_compare_col;                   // 要排序的列索引
  bool m_col_isnumber;                 // 要排序的列是否全都是数字
  int m_col_gap;                       //
  bool m_autolist;                     //
 public:
  TableGroup(const char* name, bool autoList = true)
      : m_autolist(autoList),
        m_sort_ascend(true),
        m_compare_col(0),
        m_col_isnumber(false),
        m_name(name),
        m_col_gap(2) {
    m_table.push_back(new Record());  // 新的记录行
  }
  ~TableGroup() {
    for (int i = 0; i < (int)m_table.size(); i++) {
      Record* precord = m_table.at(i);
      if (precord) delete precord;
    }
    m_table.clear();
  }
  const BaseString& GetName() const { return m_name; }
  void UpdateRefColumn() {
    // 名称以@开头的是引用列,其引用对应名称的列,输出值为占比百分比
    for (int i = 0; i < (int)m_columns.size(); i++) {
      TableColumn& tc = m_columns[i];
      if (!IsRefColumn(tc.name)) continue;
      BaseString name = tc.name.substr(1, tc.name.size() - 1);
      int col = FindColumn(name);
      if (col < 0) continue;

      if (tc.name[0] == '@')
        UpdateBaseTimes(i, col, m_table);
      else if (tc.name[0] == '#')
        UpdatePercent(i, col, m_table);
    }
  }
  void AddField(const BaseString& name, const BaseString& value,
                int maxLength /* = 0 */) {
    bool newline = false;
    int col = GetColumn(name, newline);
    TableColumn& tc = m_columns[col];
    Record* precord = m_table.at(m_table.size() - 1);
    Record& record = *precord;
    if (maxLength > 0 && maxLength < value.size()) {
      int pos = value.size() - maxLength;
      BaseString cut = "...";
      cut += BaseString(value.c_str() + pos + 3, maxLength - 3);
      record[col] = cut;
      tc.maxlen = tc.maxlen < cut.size() ? cut.size() : tc.maxlen;
    } else {
      record[col] = value;
      tc.maxlen = tc.maxlen < value.size() ? value.size() : tc.maxlen;
    }
    if (newline)  // 将缺失的列的count补齐,不然会造成后续判断换行不对
    {
      for (int i = 0; i < (int)m_columns.size(); i++) {
        m_columns[i].count = 0;
      }
    }
    tc.count++;
  }
  int FindColumn(const BaseString& name) {
    IndexMap::iterator it = m_indexmap.find(name);
    if (it != m_indexmap.end()) {
      int col = it->second;
      if (col >= 0 && col < (int)m_columns.size()) {
        TableColumn& tc = m_columns[col];
        if (tc.name == name) return col;
      }
    }
    return -1;
  }
  int GetColumn(const BaseString& name, bool& newline) {
    // 获取列名称name对应的列索引,有可能会增加新列
    // newline是判断是否要增加新记录行
    IndexMap::iterator it = m_indexmap.find(name);
    if (it != m_indexmap.end()) {
      int col = it->second;
      TableColumn& tc = m_columns[col];
      newline = tc.count >= 1;
      if (newline) m_table.push_back(new Record());
      return it->second;
    }

    // 增加新的列
    TableColumn tc;
    tc.name = name;
    tc.maxlen = name.size();
    tc.count = 0;
    int col = m_columns.size();
    m_columns.push_back(tc);
    m_indexmap[name] = col;
    newline = false;
    return col;
  }
  void SortBy(const BaseString& name, bool ascend) {
    m_sort_col = name;
    m_sort_ascend = ascend;
  }
  BaseString Format() {
    // 将此表格格式化成一个字符串
    AdjustOrder();
    int namewidth = 0, valuewidth = 0;
    int capcity = CalculateCapcity(namewidth, valuewidth);
    BaseString bs(capcity);
    if (m_table.size() <= 0) return bs;
    if (m_autolist && m_table.size() <= 1) {
      Record* precord = m_table.at(0);
      for (int i = 0; i < (int)m_columns.size(); i++) {
        TableColumn& tc = m_columns.at(i);
        bs.append(tc.name);
        bs.append(namewidth - 2 - tc.name.size(), ' ');
        bs.append(": ");
        Record::iterator it = precord->find(i);
        if (it != precord->end()) {
          BaseString& val = it->second;
          bs.append(val);
          bs.append(valuewidth - val.size(), ' ');
        } else
          bs.append(valuewidth, ' ');
        bs.append("\r\n");
      }
      return bs;
    }
    for (int i = 0; i < (int)m_columns.size(); i++) {
      TableColumn& tc = m_columns.at(i);
      int colwidth = tc.maxlen + m_col_gap;
      bs.append(tc.name);
      bs.append(colwidth - tc.name.size(), ' ');
    }
    bs.append("\r\n");
    for (int j = 0; j < (int)m_table.size(); j++) {
      Record* precord = m_table.at(j);
      for (int i = 0; i < (int)m_columns.size(); i++) {
        TableColumn& tc = m_columns.at(i);
        int colwidth = tc.maxlen + m_col_gap;
        Record::iterator it = precord->find(i);
        if (it != precord->end()) {
          BaseString& val = it->second;
          bs.append(val);
          bs.append(colwidth - val.size(), ' ');
        } else
          bs.append(colwidth, ' ');
      }
      bs.append("\r\n");
    }
    return bs;
  }

 protected:
  int CalculateCapcity(int& name_width, int& value_width) {
    if (m_table.size() <= 0) {
      name_width = 0, value_width = 0;
      return 0;
    }
    if (m_autolist && m_table.size() <= 1) {
      Record* precord = m_table.at(0);
      for (int i = 0; i < (int)m_columns.size(); i++) {
        TableColumn& tc = m_columns.at(i);
        int namelen = tc.name.size();
        Record::iterator it = precord->find(i);
        int valuelen = it != precord->end() ? it->second.size() : 0;
        name_width =
            i == 0 ? namelen : (name_width < namelen ? namelen : name_width);
        value_width = i == 0
                          ? valuelen
                          : (value_width < valuelen ? valuelen : value_width);
      }
      //  2  用于绘制 ": "
      name_width += m_col_gap + 2;
      value_width += m_col_gap;
      int record_size = name_width + value_width + 1;
      return record_size * m_columns.size();
    }
    int record_size = 0;
    for (int i = 0; i < (int)m_columns.size(); i++) {
      TableColumn& tc = m_columns.at(i);
      record_size += tc.maxlen + m_col_gap;
    }
    record_size += 1;
    return record_size * m_table.size() + record_size;
  }
  void AdjustOrder() {
    if (m_sort_col.empty()) return;
    IndexMap::iterator it = m_indexmap.find(m_sort_col);
    if (it == m_indexmap.end()) return;

    // 根据列col将此排序
    // 判断列col的类型:是数字还是字符串
    m_compare_col = it->second;
    m_col_isnumber = IsColNumber(m_compare_col);
#if defined(WIN32)
    qsort_s(&m_table.at(0), m_table.size(), sizeof(Record*),
            CompareRecordStatic, this);
#elif defined(CCORE_MAC)
    struct sort_r_data tmp;
    tmp.arg = this;
    tmp.compar = CompareRecordStaticLinux;
    qsort_r(&m_table.at(0), m_table.size(), sizeof(Record*), &tmp,
            &sort_r_arg_swap);
#else
    qsort_r(&m_table.at(0), m_table.size(), sizeof(Record*),
            CompareRecordStaticLinux, this);  // TODO: MAC maybe different!!
#endif
  }
  bool IsColNumber(int col) {
    for (int i = 0; i < (int)m_table.size(); i++) {
      Record* precord = m_table.at(i);
      BaseString& val = (*precord)[col];
      if (!StringUtil::IsNumber(val)) return false;
    }
    return true;
  }
  static int CompareRecordStatic(void* context, const void* re1,
                                 const void* re2) {
    TableGroup* group = (TableGroup*)context;
    const Record* r1 = (const Record*)re1;
    const Record* r2 = (const Record*)re2;
    int cmp = group->CompareRecord(r1, r2);
    return group->m_sort_ascend ? cmp : (-cmp);
  }
  static int CompareRecordStaticLinux(const void* re1, const void* re2,
                                      void* context) {
    TableGroup* group = (TableGroup*)context;
    const Record* r1 = (const Record*)re1;
    const Record* r2 = (const Record*)re2;
    int cmp = group->CompareRecord(r1, r2);
    return group->m_sort_ascend ? cmp : (-cmp);
  }
  int CompareRecord(const Record* r1, const Record* r2) {
    Record::const_iterator rt1 = r1->find(m_compare_col);
    Record::const_iterator rt2 = r2->find(m_compare_col);
    if (rt1 == r1->end() || rt2 == r2->end()) return 0;
    const BaseString& s1 = rt1->second;
    const BaseString& s2 = rt2->second;
    if (!m_col_isnumber) {
      return s1.compare(s2.c_str(), s2.size());
    }

    double f1 = atof(s1.c_str());
    double f2 = atof(s2.c_str());
    return f1 > f2 ? 1 : (f1 < f2 ? -1 : 0);
  }
};

class TableGroupVec : public std::vector<TableGroup*> {};
FormatTable::FormatTable() : m_autolist(true), m_hasref(false) {
  m_groups = new TableGroupVec();
}

FormatTable::~FormatTable() {
  for (int i = 0; i < (int)m_groups->size(); i++) {
    TableGroup* group = m_groups->at(i);
    delete group;
  }
  m_groups->clear();
  delete m_groups;
}

void FormatTable::AddField(const BaseString& name, const BaseString& value,
                           int maxLength /*= 0*/) {
  if (m_groups->size() <= 0)
    m_groups->push_back(new TableGroup("", m_autolist));
  TableGroup* group = m_groups->at(m_groups->size() - 1);
  group->AddField(name, value, maxLength);
  if (!m_hasref) m_hasref = IsRefColumn(name);
}

void FormatTable::AddGroup(const BaseString& name) {
  TableGroup* group = new TableGroup(name, m_autolist);
  m_groups->push_back(group);
}

void FormatTable::SortBy(const BaseString& name, bool ascend) {
  if (m_groups->size() > 0) {
    TableGroup* group = m_groups->at(m_groups->size() - 1);
    group->SortBy(name, ascend);
  }
}
void FormatTable::UpdateRefColumn() {
  if (!m_hasref) return;
  for (int i = 0; i < (int)m_groups->size(); i++) {
    TableGroup* group = m_groups->at(i);
    group->UpdateRefColumn();
  }
}
qb::base::BaseString FormatTable::Format() {
  BaseString fmt;
  UpdateRefColumn();
  for (int i = 0; i < (int)m_groups->size(); i++) {
    TableGroup* group = m_groups->at(i);
    fmt += group->Format();
  }
  return fmt;
}
void FormatTable::FormatToConsole(const char* msg) {
  FormatToOutput(kOutputTypeConsole, msg);
}
void FormatTable::FormatToLog(const char* msg) {
  FormatToOutput(kOutputTypeLog, msg);
}
void FormatTable::FormatToOutput(kOutputType type, const char* msg) {
  BaseString fmt;
  UpdateRefColumn();
  for (int i = 0; i < (int)m_groups->size(); i++) {
    TableGroup* group = m_groups->at(i);
    fmt += group->Format();
  }
  char* buffer = fmt.c_str();
  int len = fmt.size();
  char* start = buffer;
  while (start) {
    char* nline = strchr(start, '\n');
    char* next = (nline && nline + 1 < buffer + len) ? (nline + 1) : 0;
    if (nline) *nline = 0;
    switch (type) {
      case kOutputTypeLog:
        //	BLOGINFO("%s%s", msg ? msg : "", start);
        break;
      case kOutputTypeConsole:
        printf("\n%s%s", msg ? msg : "", start);
        break;
      case kOutputTypeDebugView:
        //	qb::base::DebugView(0, 0, 0, "%s%s", msg ? msg : "", start);
        break;
    }
    start = next;
  }
}
void FormatTable::FormatToDebugView(const char* msg) {
  FormatToOutput(kOutputTypeDebugView, msg);
}

}  // namespace base
}  // namespace qb
